package common;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.geom.*;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;


/**
 * This frame shows the toolbar and the graph.
 */
public class GraphFrame extends JFrame {
	/**
	 * Constructs a graph frame that displays a given graph.
	 * 
	 * @param graph
	 *            the graph to display
	 */
	public GraphFrame(final Graph graph) {
		setSize(FRAME_WIDTH, FRAME_HEIGHT);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		this.graph = graph;

		constructFrameComponents();
		// set up menus

		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);
		JMenu fileMenu = new JMenu("File");
		menuBar.add(fileMenu);

		JMenuItem openItem = new JMenuItem("Open");
		openItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				openFile();
			}
		});
		fileMenu.add(openItem);

		JMenuItem saveItem = new JMenuItem("Save");
		saveItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				saveFile();
			}
		});
		fileMenu.add(saveItem);

		JMenuItem exitItem = new JMenuItem("Exit");
		exitItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				System.exit(0);
			}
		});
		fileMenu.add(exitItem);

		JMenuItem deleteItem = new JMenuItem("Delete");
		deleteItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				panel.removeSelected();
			}
		});

		JMenuItem propertiesItem = new JMenuItem("Properties");
		propertiesItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				panel.editSelected();
			}
		});

		JMenu editMenu = new JMenu("Edit");
		editMenu.add(deleteItem);
		editMenu.add(propertiesItem);
		menuBar.add(editMenu);
	}

	/**
	 * Constructs the tool bar and graph panel.
	 */
	private void constructFrameComponents() {
		toolBar = new ToolBar(graph);
		panel = new GraphPanel(toolBar, graph);
		scrollPane = new JScrollPane(panel);
		Container contentPane = getContentPane();
		contentPane.add(toolBar, BorderLayout.NORTH);
		contentPane.add(scrollPane, BorderLayout.CENTER);
	}

	/**
	 * Asks the user to open a graph file.
	 */
	private void openFile() {
		// let user select file

		JFileChooser fileChooser = new JFileChooser();
		int r = fileChooser.showOpenDialog(this);
		if (r == JFileChooser.APPROVE_OPTION) {
			// open the file that the user selected
			try {
				File file = fileChooser.getSelectedFile();
				
				SAXBuilder builder = new SAXBuilder();
				Document doc = builder.build(file);
				
				Element root = doc.getRootElement();
				Element nodesEl = root.getChild("nodes");
				Element edgesEl = root.getChild("edges");
				
				for (Element n : nodesEl.getChildren()){
					Element colorEl = n.getChild("color");
					Color color = new Color(colorEl.getAttribute("red").getIntValue(), colorEl.getAttribute("green").getIntValue(), colorEl.getAttribute("blue").getIntValue());
					Node node = new CircleNode(color);
					Point2D point = new Point2D.Double(n.getAttribute("x").getDoubleValue(), n.getAttribute("y").getDoubleValue());
					node.setID(n.getAttribute("ID").getIntValue());
					
					if (node instanceof CircleNode){
						((CircleNode) node).setDescription(n.getAttributeValue("description"));
						((CircleNode) node).setSize(n.getAttribute("size").getDoubleValue());
					}
					graph.add(node, point);
				}
				
				for (Element e : edgesEl.getChildren()){
					Edge edge = null;
					if (e.getAttribute("radius") != null){
						edge = new ArcEdge();
						((ArcEdge)edge).setRadius(e.getAttribute("radius").getDoubleValue());
						((ArcEdge)edge).setSide(e.getAttribute("side").getBooleanValue());
						
					} else {
						edge = new LineEdge();
					}
					
					Node[] nodes = new Node[graph.getNodes().size()];
					graph.getNodes().toArray(nodes);
					Node start = null;
					Node end = null;
					int startID = e.getAttribute("start").getIntValue();
					int endID = e.getAttribute("end").getIntValue();
					for(Node node : nodes){
						if (node.getID() == startID) start = node;
						if (node.getID() == endID) end = node;
					}
					if (start != null && end != null){
						edge.connect(start, end);
						Point2D p1 = new Point2D.Double(start.getBounds().getCenterX(), start.getBounds().getCenterY());
						Point2D p2 = new Point2D.Double(end.getBounds().getCenterX(), end.getBounds().getCenterY());
						graph.connect(edge, p1, p2);
					}
				}
				
				Container contentPane = getContentPane();
				contentPane.remove(scrollPane);
				contentPane.remove(toolBar);
				constructFrameComponents();
				validate();
				repaint();
			} catch (IOException exception) {
				JOptionPane.showMessageDialog(null, exception);
			} catch (JDOMException exception) {
				JOptionPane.showMessageDialog(null, exception);
			}
		}
	}

	/**
	 * Saves the current graph in a file.
	 */
	private void saveFile() {
		JFileChooser fileChooser = new JFileChooser();
		if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
			try {
				File file = fileChooser.getSelectedFile();
				
				Element root = new Element("graph");
				Document doc = new Document(root);
				
				Node[] nodes = new Node[graph.getNodes().size()];
				graph.getNodes().toArray(nodes);
				Element eNodes = new Element("nodes");
				root.addContent(eNodes);
				for (Node n : nodes){
					Element node = new Element("node");
					if (n instanceof CircleNode){
						CircleNode n2 = (CircleNode) n;
						Color nColor = n2.getColor();
						Element color = new Element("color");
						color.setAttribute("red", Integer.toString(nColor.getRed()));
						color.setAttribute("green", Integer.toString(nColor.getGreen()));
						color.setAttribute("blue", Integer.toString(nColor.getBlue()));
						
						node.addContent(color);
						
						node.setAttribute("description", n2.getDescription());
						node.setAttribute("size", Double.toString(n2.getSize()));
					}
					
					node.setAttribute("x", Double.toString(n.getX()));
					node.setAttribute("y", Double.toString(n.getY()));
					node.setAttribute("ID", Integer.toString(n.getID()));
					
					
					eNodes.addContent(node);
				}
				
				Edge[] edges = new Edge[graph.getEdges().size()];
				graph.getEdges().toArray(edges);
				Element eEdges = new Element("edges");
				root.addContent(eEdges);
				for (Edge e : edges){
					Element edge = new Element("edge");
					edge.setAttribute("start", Integer.toString(e.getStart().getID()));
					edge.setAttribute("end", Integer.toString(e.getEnd().getID()));
					
					if (e instanceof ArcEdge){
						ArcEdge e2 = (ArcEdge) e;
						edge.setAttribute("radius", Double.toString(e2.getRadius()));
						edge.setAttribute("side", Boolean.toString(e2.getSide()));
					}
					
					eEdges.addContent(edge);
				}
				
				XMLOutputter out = new XMLOutputter(Format.getPrettyFormat());
				out.output(doc, new FileOutputStream(file));
				
			} catch (IOException exception) {
				JOptionPane.showMessageDialog(null, exception);
			}
		}
	}

	private Graph graph;
	private GraphPanel panel;
	private JScrollPane scrollPane;
	private ToolBar toolBar;

	public static final int FRAME_WIDTH = 600;
	public static final int FRAME_HEIGHT = 400;
}
